package edu.ncsu.csc;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;

import com.sun.spot.io.j2me.radiogram.RadiogramConnection;

public class BaseStationHostApp {
	private static final String REMOTE_SERVER_ADDRESS = "localhost";
	private static final int REMOTE_SERVER_PORT = 4545;
	private static final String[] MACS =
	{
		/*
		// Main SPOTs
		"0014.4F01.0000.0735",
		"0014.4F01.0000.6159",
		"0014.4F01.0000.0731",
		"0014.4F01.0000.6FE2",
		// Backup SPOTs
		"0014.4F01.0000.5558",
		"0014.4F01.0000.0629",
		"0014.4F01.0000.5EED",
		"0014.4F01.0000.700A"
		*/
			// repeaters
		"0014.4F01.0000.0D20"
	};

	private RadiogramConnection spotRadioConn;
	private Datagram dg;

	private Socket remoteServerSocket;
	private DataOutputStream remoteServerSink;

	private Statistics stats = new Statistics();
	
	public BaseStationHostApp() {
		if (!setUpRemoteServerConnection()) {
			System.exit(1);
		}
		System.out.println("Connected to remote server");

		if (!setUpRadioConnection()) {
			System.exit(1);
		}
		System.out.println("Opened radio connection");

		doPacketProcessing();
	}

	// SET UP METHODS

	private boolean setUpRadioConnection() {
		try {
			spotRadioConn = (RadiogramConnection)Connector.open("radiogram://:101");
			dg = spotRadioConn.newDatagram(spotRadioConn.getMaximumLength());
			return true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			closeConnections();
			return false;
		}
	}

	private boolean setUpRemoteServerConnection() {
		
		try {
			remoteServerSocket = new Socket(REMOTE_SERVER_ADDRESS, REMOTE_SERVER_PORT);
			remoteServerSink = new DataOutputStream(remoteServerSocket.getOutputStream());
			return true;
		} catch (IOException ioe) {
			ioe.printStackTrace();
			closeConnections();
			return false;
		}
	}

	// DO PACKET PROCESSING

	private void doPacketProcessing() {
		System.out.println("Now processing packets...");
		try	{
			while (true)
			{
				
				stats.reset();
				for(int k = 0; k < 1000; k++)
				{
					receiveDatagram(dg);
					processDatagram(dg);
				}
				stats.dumpStats();
				
			}
		} catch (IOException ioe)
		{
			ioe.printStackTrace();
			closeConnections();
		}
	}

	// RECEIVE METHODS

	private void receiveDatagram(Datagram dg) throws IOException {
		do {
			dg.reset();
			spotRadioConn.receive(dg);
		} while (!isOnListeningList(dg.getAddress()));
	}

	private boolean isOnListeningList(String address) {
		for (int i = 0; i < MACS.length; i++) {
			if (MACS[i].equals(address)) return true;
		}
		return false;
	}

	// SEND METHODS	

	private void processDatagram(Datagram dg) throws IOException {
		// Calculation of number of readings:
		// * Packet = MAC (1B) + Timestamp (8B) + Reading (2B)
		//          + Delta (2B) + Reading (2B) + ...
		//
		// * Packet size = 1 + 8 + 2 + (readings-1)*(2+2) = 11 + (readings-1)*4
		//               = 7 + readings*4
		// * readings = ((packet size) - 7)/4
		// 
		// Yay!
		//
		// Note: assumes that the number of samples in a packet is less than 127
		//

		// Calculate the number of samples in the datagram, and write this value to the remote server
		byte numSamplesLeft = (byte)((dg.getLength() - 7) / 4);
		remoteServerSink.writeByte(numSamplesLeft);

		// Read the sensorID, and relay it to the remote server
		byte sensorID = dg.readByte();
		remoteServerSink.writeByte(sensorID);

		stats.samplesReceived(sensorID, numSamplesLeft);

		// Read in the beginning timestamp and the first sample, and transmit the data
		long time = dg.readLong();
		short sample = dg.readShort();
		remoteServerSink.writeLong(time);
		remoteServerSink.writeShort(sample);
		numSamplesLeft--;

		// Continually write the last sample read, and read a new one, until there are none left
		while (numSamplesLeft > 0) {
			time += dg.readUnsignedShort();
			sample = dg.readShort();
			remoteServerSink.writeLong(time);
			remoteServerSink.writeShort(sample);
			numSamplesLeft--;
		}
	}

	// CLOSE CONNECTIONS

	private void closeConnections() {
		try {
			if (spotRadioConn != null) spotRadioConn.close();
		} catch (IOException ioe) {}

		try {
			if (remoteServerSink != null) remoteServerSink.close();
		} catch (IOException ioe) {}

		try {
			if (remoteServerSocket != null) remoteServerSink.close();
		} catch (IOException ioe) {}
	}

	// MAIN

	public static void main(String[] args) {
		new BaseStationHostApp();
	}
}
